---
title: vue3

pubDatetime: 2023-09-18T08:01:33Z
postSlug: vue3
featured: false
draft: false
tags:
  - vue3
description: "阅读 vue3 涉及与 vue3 的实现"
---

import Text from "@components/react/UI/Text.tsx";

## 阅读 vue3 涉及与 vue3 的实现

## 目录

## Response scheme of non -original value

### proxy & reflect

- proxy:代理对象

  > 语法

  <Text>const p = new Proxy(target, handler)</Text>

- reflect:提供了访问一个对象的默认行为

  > 语法

  <Text>const r = Reflect.get(target, key)</Text>

```js
const a = {};
const b = new Proxy(a, {
  get(target, key) {
    console.log("get", key);
    return Reflect.get(target, key);
  },
  set(target, key, value) {
    console.log("set", key, value);
    return Reflect.set(target, key, value);
  },
  deleteProperty(target, key) {
    console.log("delete", key);
    return Reflect.deleteProperty(target, key);
  },
});
```

#### reflect.get(target, propertyKey[, receiver])

第三个参数----如果 target 对象中指定了 getter，receiver 则为 getter 调用时的 this 值。

```js
const obj = { x: 1, y: 2 };
Reflect.get(obj, "x"); // 1

// Array
Reflect.get(["zero", "one"], 1, ["two", "another one"]); //one
```

```js
const obj = { x: 1, y: 2 };
Reflect.get(obj, "x"); // 1

// Array
Reflect.get(["zero", "one"], 1, ["two", "another one"]); // one
```

```js
const a = ["zero", "one"];
Object.defineProperty(a, "1", {
  get: function () {
    return this.at(0);
  },
});
Reflect.get(a, 1, ["one", "one"]); //one
a; // ["zero","zero"]
```

#### [The interviewer Getter in JS](https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Functions/get)

#### Section 4 The responsive data code implemented

```js
const obj = {foo:1}

const p = new Proxy(obj, {
    get(target, key) {
        track(target, key)
        return target[key]
        }

    set(target, key, value){
        target[key] = value;
        trigger(target, key)
    }
    ...
})
```

如果我们的响应式数据 obj 是这样子的

```js
const obj = {
  foo: 1,
  get bar() {
    return this.foo + 1;
  },
};
```

当我们在副作用函数:

```js
effect(() => {
  console.log(p.bar);
});
```

尝试修改 p.foo 的值，p.bar 的值也会跟着变化,但副作用函数并不会执行。

```js
p.foo = 2;
//p.bar===3
```

因为代理过程的 get 中的 target 实际上指向的是 obj

```js
const p = new Proxy(obj, {
    get(target, key) {
        //target===obj
        track(target, key)
        return target[key]
        }
    ...
})
```

解决办法

```js
const p = new Proxy(obj, {
    get(target, key,receiver) {
        //target===obj
        track(target, key)
        return Reflect.get(target,key,receiver)
        }
    ...
})
```

p 访问 bar 时，这里的 receiver 指向 p，返回 Reflect.get(target,key,receiver)时触发 get 中的 this 也就指向 p

### [vuejs Challenge ... simple](https://cn-vuejs-challenges.netlify.app/questions/10-lifecycle/README.zh-CN.html)

#### 1.life cycle

onMounted()
onUpdated()
onUnmounted()
onBeforeMount()
onBeforeUpdate()
onBeforeUnmount()
onErrorCaptured()
onRenderTracked()
onRenderTriggered()
onActivated()
onDeactivated()
onServerPrefetch()

#### 2.Next DOM update

nextTick()

#### 3.DOM Portal

[\<Teleport>](https://vuejs.org/api/built-in-components.html#teleport)

Vue 3 引入了\<Teleport>组件，这是一个非常有用的功能，它允许您将组件的内容移动到 DOM 中的其他位置，而无需改变其在组件树中的位置。这在创建模态框、下拉菜单或其他需要将内容移动到特定位置的组件时特别有用。

```vue
<script setup>
const msg = "Hello World";
</script>

<template>
  <!-- Renders it to a child element of the `body` -->
  <Teleport to="body"
    ><span>{{ msg }}</span></Teleport
  >
</template>
```

#### 4.dynamic css

[v-bind in css](https://vuejs.org/api/sfc-css-features.html#v-bind-in-css)

```vue
<script setup>
import { ref } from "vue";
const theme = ref("red");

const colors = ["blue", "yellow", "red", "green"];

setInterval(() => {
  theme.value = colors[Math.floor(Math.random() * 4)];
}, 1000);
</script>

<template>
  <p>hello</p>
</template>

<style scoped>
/* Modify the code to bind the dynamic color */
p {
  color: v-bind("theme");
}
</style>
```

#### 5.ref Family bucket

[reactivity utilities](https://vuejs.org/api/reactivity-utilities.html#isref)

```vue
<script setup lang="ts">
import { ref, Ref, reactive, isRef, toRef, unref } from "vue";

const initial = ref(10);
const count = ref(0);

// Challenge 1: Update ref
function update(value) {
  // impl...
  count.value = value;
}

/**
 * Challenge 2: Check if the `count` is a ref object.
 * Make the output be 1
 */
console.log(isRef(count) ? 1 : 0);

/**
 * Challenge 3: Unwrap ref
 * Make the output be true
 */
function initialCount(value: number | Ref<number>) {
  // Make the output be true
  console.log(unref(value) === 10);
}

initialCount(initial);

/**
 * Challenge 4:
 * create a ref for a property on a source reactive object.
 * The created ref is synced with its source property:
 * mutating the source property will update the ref, and vice-versa.
 * Make the output be true
 */
const state = reactive({
  foo: 1,
  bar: 2,
});
const fooRef = toRef(state, "foo"); // change the impl...

// mutating the ref updates the original
fooRef.value++;
console.log(state.foo === 2);

// mutating the original also updates the ref
state.foo++;
console.log(fooRef.value === 3);
</script>

<template>
  <div>
    <p>
      <span @click="update(count - 1)">-</span>
      {{ count }}
      <span @click="update(count + 1)">+</span>
    </p>
  </div>
</template>
```

#### 6.Prevent incident bubbling

使用.stop

关于[事件修饰符](https://vuejs.org/guide/essentials/event-handling.html#event-modifiers)

<details>
<summary>ai总结</summary>
<article>
 <blockquote>

在 Vue 3 中，事件修饰符是一种可以用来调整或增强事件监听行为的特殊标记。这些修饰符通常使用.前缀来应用于 v-on 指令。以下是一些常用的事件修饰符及其用途：

.stop：

阻止事件冒泡。
示例：\<button @click.stop="handleClick">点击我\</button>

.prevent：

阻止事件的默认行为。
示例：\<form @submit.prevent="handleSubmit">提交\</form>

.capture：

使用事件捕获阶段而非冒泡阶段进行监听。
示例：\<div @click.capture="handleCapture">监听捕获阶段的点击\</div>

.self：

只当事件是从原始目标（而非子元素）发起时触发监听器时才触发。
示例：\<div @click.self="handleClick">仅在点击 div 本身时触发\</div>

.once：

监听器只会在首次触发时执行，之后将被自动移除。
示例：\<button @click.once="handleOnceClick">只触发一次的点击\</button>

.passive：

使用 addEventListener 的\{ passive: true }选项，以告知浏览器监听器不会调用 preventDefault。
主要用于提高触摸事件的性能。
示例：\<div @touchstart.passive="handleTouchStart">触摸开始\</div>

.native：

监听组件根元素的原生事件，而不是组件触发的事件。
示例：\<my-component @hook:mounted.native="handleNativeMounted">组件的挂载\</my-component>
这些事件修饰符可以组合使用，例如：\<div @click.stop.prevent="handleClick">。在某些情况下，修饰符的顺序可能会影响行为，因此请确保正确地使用它们以实现所需的事件监听行为

 </blockquote>

</article>
</details>

```vue
<script setup lang="ts">
const click1 = () => {
  console.log("click1");
};

const click2 = () => {
  console.log("click2");
};
</script>

<template>
  <div @click="click1()">
    <div @click.stop="click2()">click me</div>
  </div>
</template>
```

#### 7.Response loss (using ToreFS)

[toRefs](https://cn.vuejs.org/api/reactivity-utilities.html#torefs)

```vue
<script setup lang="ts">
import { reactive } from "vue";

function useCount() {
  const state = reactive({
    count: 0,
  });

  function update(value: number) {
    state.count = value;
  }

  return {
    state,
    update,
  };
}

// 确保解构不丢失响应性
const {
  state: { count },
  update,
} = useCount();
</script>

<template>
  <div>
    <p>
      <span @click="update(count - 1)">-</span>
      {{ count }}
      <span @click="update(count + 1)">+</span>
    </p>
  </div>
</template>
```

解决办法

```vue
<script setup lang="ts">
import { reactive, toRefs } from "vue";

function useCount() {
  const state = reactive({
    count: 0,
  });

  function update(value: number) {
    state.count = value;
  }

  return {
    state: toRefs(state),
    update,
  };
}
const {
  state: { count },
  update,
} = useCount();
</script>

<template>
  <div>
    <p>
      <span @click="update(count - 1)">-</span>
      {{ count }}hhhh
      <span @click="update(count + 1)">+</span>
    </p>
  </div>
</template>
```

#### 8.[capital](https://cn-vuejs-challenges.netlify.app/questions/305-capitalize/README.zh-CN.html)

```vue
<script setup>
import { ref, vModelText } from "vue";

const value = ref("");
vModelText.beforeUpdate = (el, binding) => {
  if (el.value && binding.modifiers.capitalize) {
    el.value = el.value.charAt(0).toUpperCase() + el.value.slice(1);
  }
};
</script>

<template>
  <input type="text" v-model.capitalize="value" />
</template>
```

import ToUpper from "@components/vue/challenge/toUpperCase.vue";

<ToUpper client:load />

#### 9.Writable calculation attribute computed=>writable

```vue
<script setup lang="ts">
import { ref, computed } from "vue";

const count = ref(1);
const plusOne = computed(() => count.value + 1);

/**
 * Make the `plusOne` writable.
 * So that we can get the result `plusOne` to be 3, and `count` to be 2.
 */

plusOne.value++;
</script>

<template>
  <div>
    <p>{{ count }}</p>
    <p>{{ plusOne }}</p>
  </div>
</template>
```

import MakePlusWritable from "@components/vue/challenge/makePlusWritable.vue";

<MakePlusWritable client:load />

<details class="pt-10" open>

<summary>查看vue代码答案</summary>
```vue
<script setup lang="ts">
import { ref,  computed, onUnmounted } from "vue"

const count = ref(1)

/\*\*

- Make the `plusOne` writable.
- So that we can get the result `plusOne` to be 3, and `count` to be 2.
  \*/
  const plusOne = computed({
  get: () => count.value + 1,
  set: (val) => {
  count.value = val - 1;
  },
  });

let timer = setInterval(()=>{
plusOne.value++
},2000)

const stop = ()=>{
clearInterval(timer)
}

onUnmounted(()=>{
clearInterval(timer)
})

</script>

<template>
    <div>
        <p>count:{{ count }}</p>
        <p>plusOne{{ plusOne }}</p>
        <button @click="stop">stop</button>
    </div>
</template>

````
</details>


#### 10.watch Family bucket

```vue
<script setup lang="ts">
import { ref, watch } from 'vue';

const count = ref(0);

/**
 * Challenge 1: Watch once
 * Make sure the watch callback only triggers once
 */
const unWatch = watch(count, () => {
  console.log('Only triggered once');
  unWatch();
});

count.value = 1;
setTimeout(() => (count.value = 2));

/**
 * Challenge 2: Watch object
 * Make sure the watch callback is triggered
 */
const state = ref({
  count: 0,
});

watch(
  state,
  () => {
    console.log('The state.count updated');
  },
  { deep: true }
);

state.value.count = 2;

/**
 * Challenge 3: Callback Flush Timing
 * Make sure visited the updated eleRef
 */

const eleRef = ref();
const age = ref(2);
watch(
  age,
  () => {
    console.log(eleRef.value);
  },
  { flush: 'post' }
);
//设置 flush: 'post' 将会使侦听器延迟到组件渲染之后再执行。
age.value = 18;
</script>

<template>
  <div>
    <p>
      {{ count }}
    </p>
    <p ref="eleRef">
      {{ age }}
    </p>
  </div>
</template>
````

import Watchfamily from "@components/vue/challenge/watchfamily.vue";

<Watchfamily client:load />

#### 10.shallowRef Residential shallow layerref

```vue
<script setup lang="ts">
import { shallowRef, watch } from "vue";

const state = shallowRef({ count: 1 });

// Does NOT trigger
watch(
  state,
  () => {
    console.log("State.count Updated");
  },
  { deep: true }
);

/**
 * Modify the code so that we can make the watch callback trigger.
 */
state.value = {
  count: 2,
};
</script>
<template>
  <div>
    <p>
      {{ state.count }}
    </p>
  </div>
</template>
```

#### 11.Dependence

```vue
// Child.vue

<script setup lang="ts">
// 添加代码,使`count`值注入子组件
</script>

<template>
  {{ count }}
</template>
```

import InjectChallenge from "@components/vue/challenge/provide.vue";

<InjectChallenge client:load />

### medium

#### 1.Optimized instructions

[v-once](https://cn.vuejs.org/api/built-in-directives.html#v-once)

import Once from "@components/vue/challenge/once.vue";

<Once client:load />

#### 2.Switch

import Toggle from "@components/vue/challenge/toggle.vue";

<Toggle client:load />

#### 3.util

import Util from "@components/vue/challenge/util.vue";

<Util client:load />

#### 4.counter

import Counter from "@components/vue/challenge/counter.vue";

<Counter client:load />

#### 5.localstorage

**实现本地存储**

import Storage from "@components/vue/challenge/storage.vue";

<Storage client:load client:only="vue" />

<blockquote>
<details>
<summary>2024/01/08 vue3中的customRef</summary>

在 Vue 3 中，`customRef` 是一个新的响应式引用工具，它允许你自定义依赖跟踪。但要注意的是，`customRef` 主要用于特定的高级场景，大多数情况下，你不需要使用它。

如果你想使用 `customRef` 和 `watch` 结合起来，首先你需要理解 `customRef` 的工作方式。`customRef` 接受一个函数作为其唯一参数，这个函数会在每次访问引用时被调用，它应该返回一个值和一个依赖数组。

以下是一个简单的例子，使用 `customRef` 创建一个计数器，并使用 `watch` 监听其值：

```javascript
import { customRef, watch } from "vue";

function useCustomCounter(initialValue) {
  let count = initialValue;

  return customRef((track, trigger) => {
    return {
      get() {
        track(); // 声明依赖
        return count;
      },
      set(newValue) {
        if (newValue !== count) {
          count = newValue;
          trigger(); // 触发更新
        }
      },
    };
  });
}

export default {
  setup() {
    const counter = useCustomCounter(0);

    watch(counter, (newValue, oldValue) => {
      console.log(`Counter changed from ${oldValue} to ${newValue}`);
    });

    return {
      counter,
    };
  },
};
```

在上述代码中，我们创建了一个自定义的响应式引用 `counter`，然后使用 `watch` 监听其变化。

总结一下，`customRef` 让你有更多的灵活性来定义自己的响应式逻辑，但在实际开发中，通常只有在特定的高级场景下才需要使用它。

</details>
</blockquote>

#### 6.focus directives

切换焦点指令

import Focus from "@components/vue/challenge/vFocus.vue";

<Focus client:load />

```vue
<script setup lang="ts">
import type { AnyAaaaRecord } from "dns";
import { onUnmounted, ref } from "vue";

const state = ref(false);

/**
 * 实现一个自定义指令,让元素获取焦点
 * 确保当切换`state`时,元素随着状态值获取/失去焦点
 *
 */
const VFocus = {
  updated: (el, binding) => (binding.value ? el.focus() : el.blur()),
};
const timer = setInterval(() => {
  state.value = !state.value;
}, 2000);

onUnmounted(() => {
  clearInterval(timer);
});
</script>

<template>
  <div class="xing-cao">
    <input v-focus="state" type="text" class="focus:bg-skin-blue" />
    <p class="text-skin-base">{{ state ? "focus" : "blur" }}</p>
  </div>
</template>
```

#### 7.debounce click directives

防抖点击指令

import Debounce from "@components/vue/challenge/vDebounceClick.vue";

<Debounce client:only="vue" />

```vue
<script setup lang="ts">
import { onMounted, onUnmounted, ref } from "vue";

const state = ref(false);
const input = ref(null);

/**
 * 实现一个自定义指令,让元素获取焦点
 * 确保当切换`state`时,元素随着状态值获取/失去焦点
 *
 */
const VFocus = {
  updated: (el, binding) => {
    binding.value ? el.focus() : el.blur();
  },
};
let timer = setInterval(() => {
  state.value = !state.value;
}, 2000);

onUnmounted(() => {
  clearInterval(timer);
});

onMounted(() => {
  const obs = new IntersectionObserver(entries => {
    if (!entries || entries.length === 0) return;

    if (entries[0].isIntersecting) {
      clearInterval(timer);
      timer = setInterval(() => {
        state.value = !state.value;
      }, 2000);
    } else {
      clearInterval(timer);
    }
  });
  obs.observe(input.value);
});
</script>

<template>
  <div class="xing-cao">
    <input v-focus="state" ref="input" type="text" class="focus:bg-skin-blue" />
    <p class="text-skin-base">{{ state ? "focus" : "blur" }}</p>
  </div>
</template>
```

## Link

- [vuejs 挑战](https://cn-vuejs-challenges.netlify.app/)
- [vuejs api](https://vuejs.org/api/)
